#pragma once 


#include <optional>

namespace  helper {

enum ControlFlowOpCode {
  BREAK,
  CONTINUE,
};

class ControlFlowFlag{
public:
  ControlFlowFlag():_opCode(ControlFlowOpCode::BREAK) {}

  ControlFlowFlag(const ControlFlowOpCode & opCode) : _opCode(opCode){}
  ~ControlFlowFlag() = default;

  ControlFlowFlag(const ControlFlowFlag & other) : _opCode(other._opCode){}

  static ControlFlowFlag Break(ControlFlowOpCode opCode = ControlFlowOpCode::BREAK ){
    return ControlFlowFlag(ControlFlowOpCode::BREAK);
  }
  static ControlFlowFlag Continue(ControlFlowOpCode opCode = ControlFlowOpCode::CONTINUE){
    return ControlFlowFlag(ControlFlowOpCode::CONTINUE);
  }

  bool isBreak() const {return _opCode == ControlFlowOpCode::BREAK;} 
  bool isContinue() const {return _opCode == ControlFlowOpCode::CONTINUE;}

  const ControlFlowOpCode & opCode() const {
    return _opCode;
  }

  bool operator == (const ControlFlowFlag & other) const{
    return _opCode == other._opCode;
  }
  bool operator !=(const ControlFlowFlag & other) const {
    return _opCode != other._opCode;
  }

  std::optional<ControlFlowOpCode> breakValue() const {
    return std::make_optional<ControlFlowOpCode>(_opCode);
  }
  std::optional<ControlFlowOpCode> continueValue() const {
    return std::make_optional<ControlFlowOpCode>(_opCode);
  }

  
private:
  ControlFlowOpCode _opCode;
};

template<typename B=int, typename C=int>
class ControlFlow {
private:

  ControlFlow(const ControlFlowOpCode & opCode, const B & b, const C & c) 
  : _opCode(opCode)
  , _b(b)
  , _c(c)
  {
  }
public:
  ~ControlFlow() = default;

  ControlFlow & operator=(const ControlFlow & others) {
    _opCode = others._opCode;
    _b = others._b;
    _c = others._c;
    return *this;
  }

  static ControlFlow<B,C> Break(const B & b = B()){
    return ControlFlow<B,C>(ControlFlowOpCode::BREAK, b, C());
  }

  static ControlFlow<B,C> Continue(const C & c = C()) {
    return ControlFlow<B, C>(ControlFlowOpCode::CONTINUE, B(), c);
  }

  bool isBreak() const{
    return _opCode == ControlFlowOpCode :: BREAK;
  }
  bool isContinue() const {
    return _opCode == ControlFlowOpCode::CONTINUE;
  }
  
  ControlFlowOpCode opCode(){
    return _opCode;
  }

//  B & breakValueRaw() const {
//    return _b;
//  }
//  C & continueValueRaw() const {
//    return _c;
//  }

  std::optional<B> breakValue() const {
    return this->isBreak() ? std::make_optional<B>(_b) : std::nullopt;
  }
  std::optional<C> continueValue() const {
    return isContinue() ? std::make_optional<C>(_c) : std::nullopt;
  }

  template<typename T, typename F = std::function<T(const B &)>> 
  ControlFlow<T, C> map_break(const F & f) {
    return ControlFlow<T,C>::Break(f(_b));
  }

private:
  ControlFlowOpCode _opCode;
  B _b; 
  C _c;
};

}// namespace helper
